package orm

import (
	"fmt"
	"gitee.com/lv_baobao/gcore/helper"
	"go/ast"
	"reflect"
	"strings"
	"time"
)

type Schema struct {
	Name        string
	ModelType   reflect.Type
	Value       reflect.Value
	NumField    int
	FieldNames  []string
	Fields      []Field
	PkField     *Field
	AutoPkField *Field
}
type Field struct {
	Name     string
	Type     reflect.Type
	Tag      string
	AutoIncr bool
	IsPk     bool
}

func (s *Schema) initSchema(val interface{}) {
	//value := reflect.Indirect(reflect.ValueOf(val))
	//fmt.Println(value)
	//fmt.Println(value.Type().Elem())

	value := reflect.ValueOf(val)
	s.Value = reflect.Indirect(value)
	modelType := s.Value.Type()
	if modelType.Kind() == reflect.Slice {
		modelType = modelType.Elem()
	}
	s.ModelType = modelType
	s.Name = modelType.Name()
	s.NumField = modelType.NumField()
	s.FieldNames = make([]string, 0)
	s.Fields = make([]Field, 0)

	for i := 0; i < s.NumField; i++ {
		field := modelType.Field(i)
		if field.Anonymous || !ast.IsExported(field.Name) {
			continue
		}
		s.FieldNames = append(s.FieldNames, field.Name)
		var f = Field{}
		f.Tag = field.Tag.Get("xorm")
		f.Name = field.Name
		f.Type = field.Type
		//f.AutoIncr = f.Tag == "pk autoincr"
		f.AutoIncr = strings.Contains(f.Tag, "autoincr")
		f.IsPk = strings.Contains(f.Tag, "pk")
		s.Fields = append(s.Fields, f)
		if f.IsPk {
			s.PkField = &f
		}
		if f.AutoIncr {
			s.AutoPkField = &f
		}
	}
	//var pkField *Field
	//for _, field := range s.Fields {
	//	if field.IsPk {
	//		pkField = new(Field)
	//		pkField.AutoIncr = true
	//		pkField.Name = field.Name
	//		pkField.Tag = field.Tag
	//	}
	//}
}

func (s *Schema) GetInsertRecordValues(dest interface{}) []interface{} {

	var destValue reflect.Value
	if dest == nil {
		destValue = s.Value
	} else {
		destValue = reflect.Indirect(reflect.ValueOf(dest))
	}

	var fieldValues []interface{}
	for _, field := range s.Fields {
		if field.AutoIncr {
			continue
		}
		fieldValue := s.GetFieldValue(destValue, field)
		fieldValues = append(fieldValues, fieldValue)
	}
	return fieldValues
}

func (s *Schema) GetRecordValues(dest interface{}) []interface{} {

	var destValue reflect.Value
	if dest == nil {
		destValue = s.Value
	} else {
		destValue = reflect.Indirect(reflect.ValueOf(dest))
	}

	var fieldValues []interface{}
	for _, field := range s.Fields {
		fieldValue := s.GetFieldValue(destValue, field)
		fieldValues = append(fieldValues, fieldValue)
	}
	return fieldValues
}

func (s *Schema) GetUpdateRecordValues(dest interface{}) []interface{} {
	//destValue := reflect.Indirect(reflect.ValueOf(dest))
	var fieldValues []interface{}
	//var pkField *Field = s.PkField
	for _, field := range s.Fields {
		if field.IsPk {
			continue
		}
		fieldValue := s.GetFieldValue(s.Value, field)
		fieldValues = append(fieldValues, fieldValue)
	}
	//pkFieldValue := s.GetFieldValue(s.Value, *pkField)
	//fieldValues = append(fieldValues, pkFieldValue)
	return fieldValues
}

func (s *Schema) GetUpdateFieldNames() []string {
	//destValue := reflect.Indirect(reflect.ValueOf(dest))
	var fieldNames []string
	for _, field := range s.Fields {
		if field.IsPk {
			continue
		}
		fieldNames = append(fieldNames, field.Name)
	}
	return fieldNames
}

func equalMinTime(t time.Time) bool {
	parse, _ := time.Parse(helper.TimeFormart, "1900-01-01 00:00:00")
	var isMinTime = time.Time(t).Before(parse)
	return isMinTime
}
func (s *Schema) GetFieldValue(destValue reflect.Value, field Field) interface{} {
	fieldByName := destValue.FieldByName(field.Name)
	if fieldByName.Kind() == reflect.Struct {
		fieldType := fieldByName.Type()
		if fieldType.ConvertibleTo(reflect.TypeOf(time.Time{})) {
			t := fieldByName.Convert(reflect.TypeOf(time.Time{})).Interface().(time.Time)
			if equalMinTime(t) {
				return nil
			}
			timeValue := t.Format(helper.TimeFormart)
			return timeValue
			//return t
		}
		return nil
	} else {
		//fieldType := fieldByName.Type()
		//if fieldType.ConvertibleTo(reflect.TypeOf(int(0))) {
		//	t := fieldByName.Convert(reflect.TypeOf(int(0))).Interface().(int)
		//	timeValue := strconv.Itoa(t)
		//	return timeValue
		//}
		return fieldByName.Interface()
	}
}

func (s *Schema) GetFieldStringValue(destValue reflect.Value, field Field) interface{} {
	fieldByName := destValue.FieldByName(field.Name)
	if fieldByName.Kind() == reflect.Struct {
		fieldType := fieldByName.Type()
		if fieldType.ConvertibleTo(reflect.TypeOf(time.Time{})) {
			t := fieldByName.Convert(reflect.TypeOf(time.Time{})).Interface().(time.Time)
			timeValue := t.Format(helper.TimeFormart)
			return timeValue
		}
		return nil
	} else {
		return fmt.Sprint(fieldByName.Interface())
	}
}

func NewSchema(val interface{}) *Schema {
	s := new(Schema)
	s.initSchema(val)
	return s
}
